﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using ProjectEuler.Linq;

namespace ProjectEuler.Problems
{
    [EulerProblem(32, 45228)]
    [Description("Find the sum of all numbers that can be written as pandigital products.")]
    internal sealed class Problem032 : EulerProblem
    {
        public override Object Solve()
        {
            var set = new HashSet<Int32>();
            var permutations = FiveDigitPermutations();

            set.UnionWith(from perm in permutations
                          let a = TakeNumber(perm, 0, 2)
                          let b = TakeNumber(perm, 2, 3)
                          let p = a * b
                          where IsPandigital(a, b, p)
                          select p);

            set.UnionWith(from perm in permutations
                          let a = TakeNumber(perm, 0, 1)
                          let b = TakeNumber(perm, 1, 4)
                          let p = a * b
                          where IsPandigital(a, b, p)
                          select p);

            return set.Sum();
        }

        private static Int32[][] FiveDigitPermutations()
        {
            return (from digits in Combinations.All((1).To(9), 5)
                    from perm in digits.Permutations()
                    select perm).ToArray();
        }

        private static Boolean IsPandigital(Int32 a, Int32 b, Int32 p)
        {
            if (p < 10000)
            {
                var digits = new List<Int32>(10);

                digits.AddRange(a.DecimalDigits());
                digits.AddRange(b.DecimalDigits());
                digits.AddRange(p.DecimalDigits());

                return (1).To(9).All(d => digits.Count(i => i == d) == 1);
            }

            return false;
        }

        private static Int32 TakeNumber(Int32[] digits, Int32 index, Int32 count)
        {
            var result = digits[index + count - 1];

            if (count > 1)
            {
                var power = 10;

                for (var i = count - 2; i >= 0; i--)
                {
                    result += power * digits[index + i];
                    power *= 10;
                }
            }

            return result;
        }
    }
}
